home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / msysjour / vol07 / 02 / controls / muscroll.c < prev    next >
C/C++ Source or Header  |  1992-02-29  |  14KB  |  449 lines

  1. /*
  2.  * MUSCROLL.C
  3.  *
  4.  * Contains the main window procedure of the MicroScroll control
  5.  * that handles mouse logic, and Windows messages.
  6.  *
  7.  * Version 1.1, October 1991, Kraig Brockschmidt
  8.  */
  9.  
  10.  
  11. #include <windows.h>
  12. #include "muscrdll.h"
  13.  
  14.  
  15.  
  16. /*
  17.  * MicroScrollWndProc
  18.  *
  19.  * Purpose:
  20.  *  Window Procedure for the MicroScroll custom control.  Handles all
  21.  *  messages like WM_PAINT just as a normal application window would.
  22.  *  Any message not processed here should go to DefWindowProc.
  23.  *
  24.  * Parameters:
  25.  *  hWnd            HWND handle to the control window.
  26.  *  iMsg            WORD message identifier.
  27.  *  wParam          WORD parameter of the message.
  28.  *  lParam          LONG parameter of the message.
  29.  *
  30.  * Return Value:
  31.  *  LONG            Varies with the message.
  32.  *
  33.  */
  34.  
  35. LONG FAR PASCAL MicroScrollWndProc(HWND hWnd, WORD iMsg,
  36.                                    WORD wParam, LONG lParam)
  37.     {
  38.     PMUSCROLL       pMS;
  39.     POINT           pt;
  40.     RECT            rect;
  41.     short           x, y;
  42.     short           cx, cy;
  43.     WORD            wState;
  44.  
  45.  
  46.     /*
  47.      * Get a pointer to the MUSCROLL structure for this control.
  48.      * Note that if we do this before WM_NCCREATE where we allocate
  49.      * the memory, pMS will be NULL, which is not a problem since
  50.      * we do not access it until after WM_NCCREATE.
  51.      */
  52.     pMS=(PMUSCROLL)GetWindowWord(hWnd, GWW_MUSCROLLHMEM);
  53.  
  54.  
  55.     //Let the API handler process WM_USER+xxxx messages
  56.     if (iMsg >= WM_USER)
  57.         return LMicroScrollAPI(hWnd, iMsg, wParam, lParam, pMS);
  58.  
  59.  
  60.     //Handle standard Windows messages.
  61.     switch (iMsg)
  62.         {
  63.         case WM_NCCREATE:
  64.         case WM_CREATE:
  65.             return LMicroScrollCreate(hWnd, iMsg, pMS, (LPCREATESTRUCT)lParam);
  66.  
  67.         case WM_NCDESTROY:
  68.             //Free the control's memory.
  69.             LocalFree((HANDLE)pMS);
  70.             break;
  71.  
  72.         case WM_ERASEBKGND:
  73.             /*
  74.              * Eat this message to avoid erasing portions that
  75.              * we are going to repaint in WM_PAINT.  Part of a
  76.              * change-state-and-repaint strategy is to rely on
  77.              * WM_PAINT to do anything visual, which includes
  78.              * erasing invalid portions.  Letting WM_ERASEBKGND
  79.              * erase the background is redundant.
  80.              */
  81.             break;
  82.  
  83.  
  84.         case WM_PAINT:
  85.             return LMicroScrollPaint(hWnd, pMS);
  86.  
  87.  
  88.         case WM_ENABLE:
  89.             /*
  90.              * Handles disabling/enabling case.  Example of a
  91.              * change-state-and-repaint strategy since we let the
  92.              * painting code take care of the visuals.
  93.              */
  94.             if (wParam)
  95.                 StateClear(pMS, MUSTATE_GRAYED);
  96.             else
  97.                 StateSet(pMS, MUSTATE_GRAYED);
  98.  
  99.             //Force a repaint since the control will look different.
  100.             InvalidateRect(hWnd, NULL, TRUE);
  101.             UpdateWindow(hWnd);
  102.             break;
  103.  
  104.  
  105.         case WM_SHOWWINDOW:
  106.             /*
  107.              * Set or clear the hidden flag. Windows will
  108.              * automatically force a repaint if we become visible.
  109.              */
  110.             if (wParam)
  111.                 StateClear(pMS, MUSTATE_HIDDEN);
  112.             else
  113.                 StateSet(pMS, MUSTATE_HIDDEN);
  114.  
  115.             break;
  116.  
  117.  
  118.         case WM_CANCELMODE:
  119.             /*
  120.              * IMPORTANT MESSAGE!  WM_CANCELMODE means that a
  121.              * dialog or some other modal process has started.
  122.              * we must make sure that we cancel any clicked state
  123.              * we are in, kill the timers, and release the capture.
  124.              */
  125.             StateClear(pMS, MUSTATE_DOWNCLICK | MUSTATE_UPCLICK);
  126.             KillTimer(hWnd, IDT_FIRSTCLICK);
  127.             KillTimer(hWnd, IDT_HOLDCLICK);
  128.             ReleaseCapture();
  129.             break;
  130.  
  131.  
  132.         case WM_TIMER:
  133.             /*
  134.              * We run two timers:  the first is the initial delay
  135.              * after the first click before we begin repeating, the
  136.              * second is the repeat rate.
  137.              */
  138.             if (wParam==IDT_FIRSTCLICK)
  139.                 {
  140.                 KillTimer(hWnd, wParam);
  141.                 SetTimer(hWnd, IDT_HOLDCLICK, CTICKS_HOLDCLICK, NULL);
  142.                 }
  143.  
  144.             /*
  145.              * Send a new scroll message if the mouse is still in the
  146.              * originally clicked area.
  147.              */
  148.             if (!StateTest(pMS, MUSTATE_MOUSEOUT))
  149.                 PositionChange(hWnd, pMS);
  150.  
  151.             break;
  152.  
  153.  
  154.         case WM_LBUTTONDBLCLK:
  155.         case WM_LBUTTONDOWN:
  156.             /*
  157.              * When we get a mouse down message, we know that the mouse
  158.              * is over the control.  We then do the following steps
  159.              * to set up the new state:
  160.              *  1.  Hit-test the coordinates of the click to
  161.              *      determine in which half the click occurred.
  162.              *  2.  Set the appropriate MUSTATE_*CLICK state
  163.              *      and repaint that clicked half.  This is another
  164.              *      example of a change-state-and-repaint strategy.
  165.              *  3.  Send an initial scroll message.
  166.              *  4.  Set the mouse capture.
  167.              *  5.  Set the initial delay timer before repeating
  168.              *      the scroll message.
  169.              *
  170.              * A WM_LBUTTONDBLCLK message means that the user clicked
  171.              * the control twice in succession which we want to treat
  172.              * like WM_LBUTTONDOWN.  This is safe since we will receive
  173.              * WM_LBUTTONUP before the WM_LBUTTONDBLCLK.
  174.              */
  175.  
  176.             //Get the mouse coordinates.
  177.             x=LOWORD(lParam);
  178.             y=HIWORD(lParam);
  179.  
  180.  
  181.             /*
  182.              * Only need to hit-test the upper half for a vertical
  183.              * control or the left half for a horizontal control.
  184.              */
  185.             GetClientRect(hWnd, &rect);
  186.             cx=rect.right  >> 1;
  187.             cy=rect.bottom >> 1;
  188.  
  189.             if (MSS_VERTICAL & pMS->dwStyle)
  190.                 wState=(y > cy) ? MUSTATE_DOWNCLICK : MUSTATE_UPCLICK;
  191.             else
  192.                 wState=(x > cx) ? MUSTATE_RIGHTCLICK : MUSTATE_LEFTCLICK;
  193.  
  194.             //Change-state-and-repaint
  195.             StateSet(pMS, wState);
  196.             ClickedRectCalc(hWnd, pMS, &rect);
  197.             InvalidateRect(hWnd, &rect, TRUE);
  198.             UpdateWindow(hWnd);
  199.  
  200.             PositionChange(hWnd, pMS);
  201.             SetCapture(hWnd);
  202.             SetTimer(hWnd, IDT_FIRSTCLICK, CTICKS_FIRSTCLICK, NULL);
  203.             break;
  204.  
  205.  
  206.         case WM_MOUSEMOVE:
  207.             /*
  208.              * On WM_MOUSEMOVE messages we want to know if the mouse
  209.              * has moved out of the control when the control is in
  210.              * a clicked state.  If the control has not been clicked,
  211.              * then we have nothing to do.  Otherwise we want to set
  212.              * the MUSTATE_MOUSEOUT flag and repaint so the button
  213.              * visually comes up.
  214.              */
  215.             if (!StateTest(pMS, MUSTATE_CLICKED))
  216.                 break;
  217.  
  218.  
  219.             //Get the area we originally clicked and the new POINT
  220.             ClickedRectCalc(hWnd, pMS, &rect);
  221.             pt=MAKEPOINT(lParam);
  222.  
  223.             wState=pMS->wState;
  224.  
  225.             //Hit-Test the rectange and change the state if necessary.
  226.             if (PtInRect(&rect, pt))
  227.                 StateClear(pMS, MUSTATE_MOUSEOUT);
  228.             else
  229.                 StateSet(pMS, MUSTATE_MOUSEOUT);
  230.  
  231.             /*
  232.              * If the state changed, repaint the appropriate part of
  233.              * the control.
  234.              */
  235.             if (wState!=pMS->wState)
  236.                 {
  237.                 InvalidateRect(hWnd, &rect, TRUE);
  238.                 UpdateWindow(hWnd);
  239.                 }
  240.  
  241.             break;
  242.  
  243.  
  244.         case WM_LBUTTONUP:
  245.             /*
  246.              * A mouse button up event is much like WM_CANCELMODE since
  247.              * we have to clean out whatever state the control is in:
  248.              *  1.  Kill any repeat timers we might have created.
  249.              *  2.  Release the mouse capture.
  250.              *  3.  Clear the clicked states and repaint, another example
  251.              *      of a change-state-and-repaint strategy.
  252.              */
  253.             KillTimer(hWnd, IDT_FIRSTCLICK);
  254.             KillTimer(hWnd, IDT_HOLDCLICK);
  255.  
  256.             ReleaseCapture();
  257.  
  258.  
  259.  
  260.             /*
  261.              * Repaint if necessary, only if we are clicked AND the mouse
  262.              * is still in the boundaries of the control.
  263.              */
  264.             if (StateTest(pMS, MUSTATE_CLICKED) &&
  265.                 StateTest(pMS, ~MUSTATE_MOUSEOUT))
  266.                 {
  267.                 //Calculate the rectangle before clearing states.
  268.                 ClickedRectCalc(hWnd, pMS, &rect);
  269.  
  270.                 //Clear the states so we repaint properly.
  271.                 StateClear(pMS, MUSTATE_MOUSEOUT);
  272.                 StateClear(pMS, MUSTATE_CLICKED);
  273.  
  274.  
  275.                 InvalidateRect(hWnd, &rect, TRUE);
  276.                 UpdateWindow(hWnd);
  277.                 }
  278.  
  279.             //Insure that we clear out the states.
  280.             break;
  281.  
  282.  
  283.         default:
  284.             return DefWindowProc(hWnd, iMsg, wParam, lParam);
  285.         }
  286.  
  287.     return 0L;
  288.     }
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295. /*
  296.  * ClickedRectCalc
  297.  *
  298.  * Purpose:
  299.  *  Calculates the rectangle of the clicked region based on the
  300.  *  state flags MUSTATE_UPCLICK, MUSTATE_DOWNCLICK, MUSTATE_LEFTCLICK,
  301.  *  and MUSTATE_RIGHTLICK, depending on the style.
  302.  *
  303.  * Parameter:
  304.  *  hWnd            HWND handle to the control window.
  305.  *  lpRect          LPRECT rectangle structure to fill.
  306.  *
  307.  * Return Value:
  308.  *  void
  309.  *
  310.  */
  311.  
  312. void PASCAL ClickedRectCalc(HWND hWnd, PMUSCROLL pMS, LPRECT lpRect)
  313.     {
  314.     short       cx, cy;
  315.  
  316.     GetClientRect(hWnd, lpRect);
  317.     cx=lpRect->right  >> 1;
  318.     cy=lpRect->bottom >> 1;
  319.  
  320.     if (MSS_VERTICAL & pMS->dwStyle)
  321.         {
  322.         if (StateTest(pMS, MUSTATE_DOWNCLICK))
  323.             lpRect->top=cy;
  324.  
  325.         if (StateTest(pMS, MUSTATE_UPCLICK))
  326.             lpRect->bottom=1+cy;
  327.         }
  328.     else
  329.         {
  330.         //MSS_HORIZONTAL
  331.         if (StateTest(pMS, MUSTATE_RIGHTCLICK))
  332.             lpRect->left=cx;
  333.  
  334.         if (StateTest(pMS, MUSTATE_LEFTCLICK))
  335.             lpRect->right=1+cx;
  336.         }
  337.  
  338.     return;
  339.     }
  340.  
  341.  
  342.  
  343.  
  344.  
  345. /*
  346.  * PositionChange
  347.  *
  348.  * Purpose:
  349.  *  Checks what part of the control is clicked, modifies the current
  350.  *  position accordingly (taking MSS_INVERTRANGE into account) and
  351.  *  sends an appropriate message to the associate.  For MSS_VERTICAL
  352.  *  controls we send WM_VSCROLL messages and for MSS_HORIZONTAL controls
  353.  *  we send WM_HSCROLL.
  354.  *
  355.  *  The scroll code in the message is always SB_LINEUP for the upper
  356.  *  or left half of the control (vertical and horizontal, respectively)
  357.  *  and SB_LINEDOWN for the bottom or right half.
  358.  *
  359.  *  This function does not send a message if the position is pegged
  360.  *  on the minimum or maximum of the range if MSS_NOPEGSCROLL is
  361.  *  set in the style bits.
  362.  *
  363.  * Parameters:
  364.  *  hWnd            HWND of the control.
  365.  *  pMS             PMUSCROLL pointer to control data structure.
  366.  *
  367.  * Return Value:
  368.  *  void
  369.  */
  370.  
  371. void PASCAL PositionChange(HWND hWnd, PMUSCROLL pMS)
  372.     {
  373.     WORD         wScrollMsg;
  374.     WORD         wScrollCode;
  375.     BOOL         fPegged=FALSE;
  376.  
  377.     if (StateTest(pMS, MUSTATE_UPCLICK | MUSTATE_LEFTCLICK))
  378.         wScrollCode=SB_LINEUP;
  379.  
  380.     if (StateTest(pMS, MUSTATE_DOWNCLICK | MUSTATE_RIGHTCLICK))
  381.         wScrollCode=SB_LINEDOWN;
  382.  
  383.     wScrollMsg=(MSS_VERTICAL & pMS->dwStyle) ? WM_VSCROLL : WM_HSCROLL;
  384.  
  385.     /*
  386.      * Modify the current position according to the following rules:
  387.      *
  388.      * 1. On SB_LINEUP with an inverted range, increment the position.
  389.      *    If the position is already at the maximum, set the pegged flag.
  390.      *
  391.      * 2. On SB_LINEUP with an normal range, decrement the position.
  392.      *    If the position is already at the minimum, set the pegged flag.
  393.      *
  394.      * 3. On SB_LINEDOWN with an inverted range, treat like SB_LINEUP
  395.      *    with a normal range.
  396.      *
  397.      * 4. On SB_LINEDOWN with an normal range, treat like SB_LINEUP
  398.      *    with an inverted range.
  399.      */
  400.  
  401.     if (wScrollCode==SB_LINEUP)
  402.         {
  403.         if (MSS_INVERTRANGE & pMS->dwStyle)
  404.             {
  405.             if (pMS->iPos==pMS->iMax)
  406.                 fPegged=TRUE;
  407.             else
  408.                 pMS->iPos++;
  409.             }
  410.         else
  411.             {
  412.             if (pMS->iPos==pMS->iMin)
  413.                 fPegged=TRUE;
  414.             else
  415.                 pMS->iPos--;
  416.             }
  417.         }
  418.     else
  419.         {
  420.         if (MSS_INVERTRANGE & pMS->dwStyle)
  421.             {
  422.             if (pMS->iPos==pMS->iMin)
  423.                 fPegged=TRUE;
  424.             else
  425.                 pMS->iPos--;
  426.             }
  427.         else
  428.             {
  429.             if (pMS->iPos==pMS->iMax)
  430.                 fPegged=TRUE;
  431.             else
  432.                 pMS->iPos++;
  433.             }
  434.         }
  435.  
  436.  
  437.     /*
  438.      * Send a message if we changed and are not pegged, or did not change
  439.      * and MSS_NOPEGSCROLL is clear.
  440.      */
  441.     if (!fPegged || !(MSS_NOPEGSCROLL & pMS->dwStyle))
  442.         {
  443.         SendMessage(pMS->hWndAssociate, wScrollMsg,
  444.                     wScrollCode, MAKELONG(pMS->iPos, hWnd));
  445.         }
  446.  
  447.     return;
  448.     }
  449.